package com.firefly.admin.common.annotations.aspect;


import com.alibaba.fastjson.JSON;
import com.firefly.admin.common.utils.*;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import com.firefly.admin.common.annotations.annotation.OperationLog;
import com.firefly.admin.common.constant.Constants;
import com.firefly.admin.common.service.CommonService;
import com.firefly.admin.common.vo.LoginUser;
import com.firefly.admin.common.vo.Result;
import com.firefly.admin.monitor.entity.FyMonitorErrorOperationLog;
import com.firefly.admin.monitor.service.FyMonitorOperationLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AuthorizationServiceException;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;


@Component
@Aspect
public class OperationLogAspect {
    @Autowired
    private FyMonitorOperationLogService fyMonitorOperationLogService;
    @Autowired
    private CommonService commonService;

    /**
     * 设置操作日志的切入点，用来记录操作日志，在标明注解的位置切入
     */
    @Pointcut("@annotation(com.firefly.admin.common.annotations.annotation.OperationLog)")
    public void operationLogPointCut() {

    }

    /**
     * 设置操作异常切入点记录异常日志 扫描所有controller包下操作
     */
    @Pointcut("execution(* com.firefly.admin.*.controller.*.*(..))")
    public void operErrorLogPointCut() {
    }

    /**
     * 设置操作异常切入点，拦截用户的操作日志，连接点正常执行后执行，若连接点抛出异常则不会执行
     *
     * @param joinPoint 切入点
     * @param keys      返回结果
     */
    @AfterReturning(value = "operationLogPointCut()", returning = "keys")
    public void saveOperationLog(JoinPoint joinPoint, Object keys) {
        try {
            saveOperateLog(joinPoint, keys);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 异常返回通知，用于拦截异常日志的信息，连接点抛出异常后执行
     *
     * @param joinPoint 切入点
     * @param e         异常信息
     */
    @AfterThrowing(pointcut = "operErrorLogPointCut()", throwing = "e")
    public void saveErrorLog(JoinPoint joinPoint, Throwable e) {
        try {
            FyMonitorErrorOperationLog fyMonitorErrorOperationLog = assembleErrorLogEntity(joinPoint);
            fyMonitorErrorOperationLog.setErrorName(e.getClass().getName());
            fyMonitorErrorOperationLog.setErrorMessage(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace()));
            fyMonitorOperationLogService.save(fyMonitorErrorOperationLog);
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

    public FyMonitorErrorOperationLog assembleErrorLogEntity(JoinPoint joinPoint) {
        HttpServletRequest request = commonService.getRequest();
        FyMonitorErrorOperationLog fyMonitorErrorOperationLog = new FyMonitorErrorOperationLog();
        //在切面织入点通过反射机制获取织入点的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取织入点的方法
        Method method = signature.getMethod();
        //获取操作
        OperationLog operationLog = method.getAnnotation(OperationLog.class);
        if (operationLog != null) {
            String module = operationLog.module().getModule();
            String operateDesc = operationLog.operateDesc();
            String operateType = operationLog.operateType().getType();
            fyMonitorErrorOperationLog.setOperateModule(module);
            fyMonitorErrorOperationLog.setOperateType(operateType);
            fyMonitorErrorOperationLog.setOperateDesc(operateDesc);
        }
        //请求URI
        fyMonitorErrorOperationLog.setRequestUrl(request.getRequestURI());
        String localIp = IpUtil.getLocalIp(request);
        //操作员ip地址
        fyMonitorErrorOperationLog.setRequestIp(localIp);
        //获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();
        //获取请求的方法
        String methodName = method.getName();
        methodName = className + "." + methodName;
        //请求方法
        fyMonitorErrorOperationLog.setOperateMethod(methodName);
        fyMonitorErrorOperationLog.setOperatorId(getUserId());
        Object[] paramValues = joinPoint.getArgs();
        //将参数所在的数组转为json
        String params = JSON.toJSONString(paramValues);
        //请求参数
        fyMonitorErrorOperationLog.setRequestParam(params);
        fyMonitorErrorOperationLog.setAreaAddress(AreaAddressUtil.getAreaAddress(localIp));
        fyMonitorErrorOperationLog.setSystemName(DeviceUtil.getSystemName(request));
        fyMonitorErrorOperationLog.setBrowserInfo(DeviceUtil.getBrowserInfo(request));
        //创建时间（操作时间）
        fyMonitorErrorOperationLog.setOperateTime(DateUtil.getNowDate());
        return fyMonitorErrorOperationLog;
    }

    public void saveOperateLog(JoinPoint joinPoint, Object keys) {
        HttpServletRequest request = commonService.getRequest();
        //在切面织入点通过反射机制获取织入点的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取织入点的方法
        Method method = signature.getMethod();
        //获取操作
        OperationLog operationLog = method.getAnnotation(OperationLog.class);
        String operateModule = "";
        String operateType = "";
        String operateDesc = "";
        if (operationLog != null) {
            operateModule = operationLog.module().getModule();
            operateDesc = operationLog.operateDesc();
            operateType = operationLog.operateType().getType();
        }
        String className = joinPoint.getTarget().getClass().getName();
        //获取请求的方法
        String operateMethod = method.getName();
        operateMethod = className + "." + operateMethod;
        //请求方法
        Long operatorId = getUserId();
        //返回结果
        String responseResult = JSON.toJSONString(keys);
        Result result = (Result) keys;
        String operateResult = "";
        if (result != null && result.getCode() == 200) {
            operateResult = Constants.OperateResult.SUCCESS;
        } else {
            operateResult = Constants.OperateResult.FAIL;
        }
        Object[] paramValues = joinPoint.getArgs();
        //将参数所在的数组转为json
        String requestParam = JSON.toJSONString(paramValues);
        //请求参数
        LogUtil.saveOperateLog(request, operateModule, operateType, operateDesc, operateMethod, operatorId, operateResult, responseResult, requestParam);
    }

    public Long getUserId() {
        HttpServletRequest request = commonService.getRequest();
        String authorization = request.getHeader("Authorization");
        if (StringUtils.isNotEmpty(authorization)) {
            String userKey = JwtTokenUtil.getUserKey(authorization.substring(6));
            LoginUser loginUser = (LoginUser) RedisUtil.get(userKey);
            return loginUser.getFySystemUser().getUserId();
        }
        throw new AuthorizationServiceException("未授权");
    }

    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        StringBuilder stringBuilder = new StringBuilder();
        for (StackTraceElement element : elements) {
            stringBuilder.append(element).append("\n");
        }
        return exceptionName + ":" + exceptionMessage + "\n\t" + stringBuilder;
    }
}
